package com.funo.logic.generate.code;

import cn.hutool.core.collection.CollectionUtil;
import com.funo.logic.constant.ClassEnum;
import com.funo.logic.constant.Modifier;
import com.funo.logic.generate.wrapper.AnnotationClassWrapper;
import com.funo.logic.generate.wrapper.ClassWrapper;
import com.funo.logic.generate.wrapper.GenericsClassWrapper;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * java 类代码生成
 * @since 2024年7月23日10:17:35
 * @author cms
 */
public class ClassCodeSupport extends AbstractCodeSupport implements CodeSupport {

    private String packageName;
    /**
     * java文件类型 class/interface/enum
     */
    private ClassEnum classEnum = ClassEnum.CLASS;

    private Set<AnnotationClassWrapper> annotations = new LinkedHashSet<>();

    private Set<ClassWrapper> importedTypes = new LinkedHashSet<>();

    private Set<ClassWrapper> staticImports = new LinkedHashSet<>();

    private List<Modifier> modifiers = new ArrayList<>();

    private String fullName;

    private String name;

    private GenericsClassWrapper superClass;

    private List<GenericsClassWrapper> superInterfaces = new ArrayList<>();

    private List<String> commentLines = new ArrayList<>();

    private List<FieldCodeSupport> fields = new ArrayList<>();

    private List<MethodCodeSupport> methods = new ArrayList<>();

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }


    @Override
    public String getName() {
        return name;
    }

    public void addImportedType(ClassWrapper importedType) {
        if (notContainImportedType(importedType)) {
            importedTypes.add(importedType);
        }
    }

    public void addField(FieldCodeSupport fieldCodeSupport) {
        if (notContainField(fieldCodeSupport.getName())) {
            fields.add(fieldCodeSupport);
        }
    }

    public void addModifier(Modifier modifier) {
        modifiers.add(modifier);
    }

    public void addMethod(MethodCodeSupport methodTarget) {
        if (notContainMethod(methodTarget)) {
            methods.add(methodTarget);
        }
    }
    public void addSuperInterfaces(GenericsClassWrapper superInterface) {
        if (superInterfaces.stream().noneMatch(cw -> cw.getFullName().equals(superInterface.getFullName()))) {
            superInterfaces.add(superInterface);
            addImportedType(superInterface);
        }
    }

    @Override
    public String generateCode() {
        codeBuffer.append("package ");
        codeBuffer.append(packageName);
        addSemicolon();
        addNewLine();
        addNewLine();

        if (CollectionUtil.isNotEmpty(staticImports)) {
            for (ClassWrapper staticImport : staticImports) {
                codeBuffer.append("import static ");
                codeBuffer.append(getImportName(staticImport));
                addSemicolon();
                addNewLine();
            }
            addNewLine();
        }

        if (CollectionUtil.isNotEmpty(importedTypes)) {
            for (ClassWrapper importType : importedTypes) {
                codeBuffer.append("import " + getImportName(importType));
                addSemicolon();
                addNewLine();
            }
            addNewLine();
        }

        for (String commentLine : commentLines) {
            codeBuffer.append(commentLine);
            addNewLine();
        }

        addNewLine();
        for (AnnotationClassWrapper annotation : annotations) {
            codeBuffer.append(annotation.generateCode());
            addNewLine();
        }

        if (isPublic(modifiers)) {
            codeBuffer.append("public ");
        }

        if (isProtected(modifiers)) {
            codeBuffer.append("protected ");
        }

        if (isPrivate(modifiers)) {
            codeBuffer.append("private ");
        }

        if (isAbstract(modifiers)) {
            codeBuffer.append("abstract ");
        }

        if (isStatic(modifiers)) {
            codeBuffer.append("static ");
        }

        if (isFinal(modifiers)) {
            codeBuffer.append("final ");
        }

        if (classEnum.equals(ClassEnum.INTERFACE)) {
            codeBuffer.append("interface ");
        } else if (classEnum.equals(ClassEnum.ENUM)) {
             codeBuffer.append("enum ");
        } else {
            codeBuffer.append("class ");
        }
        codeBuffer.append(name);
        addSpace();

        if (superClass != null) {
            codeBuffer.append("extends ");
            codeBuffer.append(superClass.getName());
            if (CollectionUtil.isNotEmpty(superClass.getGenericsList())) {
                addOpenAngle();
                codeBuffer.append(superClass.getGenericsList().stream().map(ClassWrapper::getName).collect(Collectors.joining(", ")));
                addCloseAngle();
            }
            addSpace();
        }

        if (CollectionUtil.isNotEmpty(superInterfaces)) {
            codeBuffer.append("implements ");
            for (int i = 0; i < superInterfaces.size(); i++) {
                GenericsClassWrapper wrapper = superInterfaces.get(i);
                if (CollectionUtil.isEmpty(wrapper.getGenericsList())) {
                    codeBuffer.append(wrapper.getShortRawName());
                } else {
                    codeBuffer.append(wrapper.getShortRawName());
                    addOpenAngle();
                    codeBuffer.append(wrapper.getGenericsList().stream().map(ClassWrapper::getName).collect(Collectors.joining(", ")));
                    addCloseAngle();
                }
                if (i + 1 < superInterfaces.size()) {
                    codeBuffer.append(",");
                }
            }
            addSpace();
        }

        addOpenBrace();
        addNewLine();

        for (FieldCodeSupport field : fields) {
            addNewLine();
            codeBuffer.append(field.generateCode());
            addNewLine();
        }

        for (MethodCodeSupport method : methods) {
            addNewLine();
            addNewLine();
            addIndent();
            codeBuffer.append(method.generateCode());
        }

        addNewLine();
        addNewLine();
        addCloseBrace();

        return codeBuffer.toString();
    }

    private String getImportName(ClassWrapper importType) {
        int index = importType.getShortRawName().indexOf("[]");
        return index == -1 ? importType.getPackageName() + "." + importType.getShortRawName()
                : importType.getPackageName() + "." + importType.getShortRawName().substring(0, index);
    }

    private boolean notContainImportedType(ClassWrapper classWrapper) {
        return importedTypes.stream().map(this::getImportName).noneMatch(
                importName -> importName.equals(getImportName(classWrapper)));
    }

    private boolean notContainField(String name) {
        return fields.stream().noneMatch(field -> field.getName().equals(name));
    }

    private boolean notContainMethod(MethodCodeSupport methodTarget) {
        return methods.stream().noneMatch(method -> method.isSameMethod(methodTarget));
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public GenericsClassWrapper getSuperClass() {
        return superClass;
    }

    public void setSuperClass(GenericsClassWrapper superClass) {
        this.superClass = superClass;
    }

    public void setClassEnum(ClassEnum classEnum) {
        this.classEnum = classEnum;
    }

    public void addAnnotation(AnnotationClassWrapper annotation) {
        annotations.add(annotation);
        addImportedType(annotation);
    }


}
